#define json_lasterror
/*
Returns the latest error
*/

return string(global.___json_lasterror);

#define json_value
/*
Returns the value of the node on success.
Returns whatever is in argument1 on fail
*/

var obj, error;
obj = argument0;
error = argument1;

if (obj < 1)
{
    json_debug("Invalid or non-existent object passed to json_value. Returning error");
    return error;
}

if (!instance_exists(obj) || string(obj.type) != 'val')
{
    json_debug("Invalid or non-existent object passed to json_value. Returning error");
    return error;
}

return obj.keyvalue;

#define json_surf
/*
Copyright (c) 2010, Daniel Tang
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/*
Surf through the JSON object
    Returns -1 on failure
    Returns instance ID on success
*/

var obj, key;
obj = real(argument0);
key = argument1;

if (!instance_exists(obj) || string(obj.type) = '')
{
    json_debug("Invalid or non-existent object passed to json_surf. Returning -1");
    return -1;
}

switch (obj.type)
{
    case "obj":
        return ds_map_find_value(obj.keyvalue,key);
        break;
    case "arr":
        return ds_list_find_value(obj.keyvalue,key);
        break;
    case "val":
        return obj;
    default:
        json_debug("Malformed object passed to json_value. Returning -1");
        return -1;
}

#define _instance_count
/*
Returns the instance count minus JSON objects
*/
json_init();

return instance_count-instance_number(global.___json);

#define json_init
/*
Copyright (c) 2010, Daniel Tang
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
var obj, code, debug;

if (variable_global_exists("___json"))
{
    return 0;
}


/*Initialize the json object*/
obj = object_add();
/*Initialization code*/
code =
'type = "";';
/*End Initialization code*/

object_event_add(obj,ev_create,0,code);
code = /*Clean up code*/ '
switch (type)
{
    case "obj":
        for (i=ds_map_size(keyvalue); i>0; i-=1)
        {
            key = ds_map_find_first(keyvalue);
            obj = ds_map_find_value(keyvalue,key);
            with (obj) instance_destroy(); /*Destroy any instances attached*/
            ds_map_delete(keyvalue,key);
            
        }
        ds_map_destroy(keyvalue);
        break;
    case "arr":
        for (i=ds_list_size(keyvalue); i>0; i-=1)
        {
            obj = ds_list_find_value(keyvalue,1);
            with (obj) instance_destroy(); /*Destroy any instances attached*/
            ds_list_delete(keyvalue,1);
            
        }
        ds_list_destroy(keyvalue);
        break;
    case "val":
        /*No cleanup is nessecary*/
        break;
}
';
/*End Cleanup code*/
object_event_add(obj,ev_destroy,0,code);
global.___json = obj;

#define json_escape
/*
Copyright (c) 2010, Daniel Tang
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/*
Escape a JSON string
*/

var str;

str = string(argument0);
str = string_replace_all(str ,'\','\\'); /*Escape backslashes (Must be first or it'll screw everything)*/

str = string_replace_all(str ,'"','\"'); /*Escape double quotes*/
str = string_replace_all(str ,chr(8) ,'\b'); /*Escape control character backspace*/
str = string_replace_all(str ,chr(9) ,'\t'); /*Escape control character horizontal tab*/
str = string_replace_all(str ,chr(10),'\n'); /*Escape control character linefeed*/
str = string_replace_all(str ,chr(12),'\f'); /*Escape control character formfeed*/
str = string_replace_all(str ,chr(13),'\r'); /*Escape control character carriage return*/


return str;

#define json_encode
/*
Copyright (c) 2010, Daniel Tang
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/*
This function reads a JSON object and turns it into a JSON string

Args:
    0 = JSON object;

Returns JSON encoded string
*/

var obj, obj_size, curr_key, str, value ,i;

obj = argument0;
obj_size = json_obj_size(obj);

if (obj_size < 1)
{
    return "{}";
}
curr_key = "";

switch (obj.type)
{
    case "obj": str = "{"; break;
    case "arr": str = "["; break;
}

for (i=1; i<=obj_size; i=i+1)
{
    switch (obj.type)
    {
        case "obj":
            if (i=1)
            {
                curr_key = json_first_key(obj);
            }else{
                curr_key = json_next_key(obj, curr_key);
            }
            value = json_surf(obj, curr_key);
            if (value.type != 'val')
            {
                str += sprintf('"%s":%s',curr_key,json_encode(value));
            }else{
                value = json_value(value);
                if (is_string(value)) /*If it's a string, escape it and enclose in double quotes*/
                {
                    value = json_escape(value);
                    value = sprintf('"%s"', value);
                }else{ /*Otherwise, just leave it*/
                    value = string(value);
                }
                str += sprintf('"%s":%s',json_escape(curr_key),value);
            }
            
            break;
        case "arr":
            value = json_surf(obj, i-1);
            if (value.type != 'val')
            {
                str += sprintf('%s',json_encode(value));
            }else{
                value = json_value(value);
                if (is_string(value)) /*If it's a string, escape it and enclose in double quotes*/
                {
                    value = json_escape(value);
                    value = sprintf('"%s"', value);
                }else{ /*Otherwise, just leave it*/
                    value = string(value);
                }
                str += sprintf('%s',value);
            }
            break;
    }
    if (i != obj_size) /*Check whether it's the last iteration*/
    {
        str += ","; /*If not, append a comma*/
    }
}

switch (obj.type)
{
    case "obj": str += "}"; break;
    case "arr": str += "]"; break;
}

return str;


#define sprintf
/*
This is a very, extremely minimal sprintf implementation for GML.

Purely for readablility.
*/

var str, num_occur, i, str_param;
str_param = "%s";

str = argument0;
num_occur = string_count(str_param, str);

for (i=0;i < num_occur;i=i+1)
{
    str = string_replace(str, str_param, argument[i+1]);
}

return str;



#define json_debug
/*
Adds a debugging message
*/

global.___json_lasterror = string(argument0);
show_debug_message("JSON: "+string(argument0));


#define json_char_type
/*
Copyright (c) 2010, Daniel Tang
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
Returns current character type
Returns:
    -1 = wtf
    0  = whitespace
    1  = number
    2  = string
    3  = object
    4  = array
    5  = comma
    6  = delimiter
*/

var char;

char = string_char_at(string(argument0),1);

switch char /*Get basics out the way*/
{
    case " ":
        return 0;
    case '"':
        return 2;
    case "{":
        return 3;
    case "[":
        return 4;
    case ",":
        return 5;
    case ":":
        return 6;
}

numbers = "0123456789.eE-+";

if (string_count(char,numbers))
{
    return 1;
}

json_debug("Warning: Unknown character '"+char+"'. Doesn't nessecarily indicate error condition",false);
return -1;


#define json_parse_string
/*
Copyright (c) 2010, Daniel Tang
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*

*/
var str, strpos_ptr, i, rtn_str, escape_mode, curr_char;
str = argument0;
i = real(argument1);
strpos_ptr = argument2;

i = json_skip_whitespace(str,i) /*Skip whitespace to be sure*/
if (i = -1)
{
    variable_local_set(strpos_ptr, -1);
    return "";
}
rtn_str = "";

escape_mode = false;


if (string_char_at(str,i) != '"')
{
    json_debug("Unexpected character '"+string_char_at(str,i)+"' at "+string(i));
    variable_local_set(strpos_ptr, -1);
    return "";
}

i+=1; /*Increment past the quotation mark*/

while (1)
{
    if (i > string_length(str))
    {
        json_debug("Unexpected end of string");
        variable_local_set(strpos_ptr, -1);
        return "";
    }
    curr_char = string_char_at(str,i);
    if (curr_char = '"' && !escape_mode) /*Check for quotation mark*/
    {
        /*We're done parsing!*/
        variable_local_set(strpos_ptr, i+1);
        return rtn_str;
    }
    
    if (escape_mode) /*Unescape if in escape mode*/
    {
        switch curr_char
        {
        case '"':
            rtn_str += '"';
            break;
        case "\":
            rtn_str += "\";
            break;
        case "b":
            rtn_str += chr(8);
            break;
        case "t":
            rtn_str += chr(9);
            break;
        case "n":
            rtn_str += chr(10);
            break;
        case "f":
            rtn_str += chr(12);
            break;
        case "r":
            rtn_str += chr(13);
            break;
        default:
            rtn_str += "\"+curr_char;
            break;
        }
        escape_mode = false; /*Not in escape mode any more*/
    }else{
    /*Otherwise, process as normal*/
        switch curr_char
        {
        case "\":
            escape_mode = true;
            break;
        default:
            rtn_str += curr_char;
        }
    }
    i+=1;
}

#define json_parse_number
/*
Copyright (c) 2010, Daniel Tang
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
Returns:
    [0] = Number                    push'd 3rd
    [1] = New string position       push'd 2nd
    [2] = Error (0 means no error)  push'd 1st

Error:
    1 = unknown character
    
    3 = end of string
*/
var str, i, rtn, curr_char, powerof, temp;

str = string(argument0);
i = real(argument1);
strpos_ptr = argument2;

i = json_skip_whitespace(str,i) /*Skip whitespace to be sure*/
if (i = -1)
{
    variable_local_set(strpos_ptr, -1);
    return 0;
}
rtn = "";

if (json_char_type(string_char_at(str,i)) != 1)
{
    json_debug("Unexpected character '"+string_char_at(str,i)+"' at "+string(i));
    variable_local_set(strpos_ptr, -1);
    return 0;
}

powerof = false;
temp = "";
while (1)
{
    curr_char = string_lower(string_char_at(str,i));
    
    if (json_char_type(curr_char)) != 1 || (i > string_length(str))
    {
        break;
    }
    if (curr_char = "e" && !powerof)
    {
        powerof = true;
        temp = rtn;
        rtn = "";
    }else{
        rtn += curr_char;
    }
    i=i+1
}

if (powerof)
{
    rtn = real(temp) * power(10, real(rtn));
}

variable_local_set(strpos_ptr, i);
return real(rtn);

#define json_skip_comma
/*
Alias of json_skip(5)
*/

var str, str_pos;
str = argument0;
str_pos = real(argument1);

return json_skip(str,str_pos,5);

#define json_skip_delimiter
/*
Alias of json_skip(6)
*/

var str, str_pos;
str = argument0;
str_pos = real(argument1);

return json_skip(str,str_pos,6);

#define json_skip
/*
Copyright (c) 2010, Daniel Tang
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
Skips the specified type
    Returns new string position on success.
    -1 on failure
*/
var str, str_pos, type;
str = argument0;
str_pos = real(argument1);
type = real(argument2);

str_pos = json_skip_whitespace(str,str_pos);
if (str_pos = -1)
{
    json_debug("Unexpected end of string");
    return -1;
}

if (json_char_type(string_char_at(str,str_pos)) != type)
{
    return -1;
}
return str_pos+1;

#define json_skip_whitespace
/*
Copyright (c) 2010, Daniel Tang
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
Skips whitespace starting at the string position specified in 2nd argument
    Returns -1 on error or string position on success
*/
var str, str_pos;
str = argument0;
str_pos = real(argument1);

while (json_char_type(string_char_at(str,str_pos)) = 0)
{
    str_pos+=1;
    if (str_pos > string_length(str))
    {
        return -1;
    }
    
}

return str_pos;

#define json_parse_object
/*
Copyright (c) 2010, Daniel Tang
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
Returns: -1 on failure, object pointer on success

Stack return:
    [0] New string position push'd first
*/
var str, str_pos, strpos_ptr, level, obj, curr_char, param, value, wantout, expect, temp;
str = argument0;
str_pos = real(argument1);
strpos_ptr = string(argument2);


str_pos = json_skip_whitespace(str, str_pos);
if (str_pos = -1)
{
    return -1;
}

if (json_char_type(string_char_at(str, str_pos)) != 3)
{
    return -1;
}
str_pos+=1;

obj = json_new_object("obj"); /*Create an object at current level*/
if (obj = -1)
{
    return -1;
}
curr_char = "";
expect = 0; /*0 = expecting a parameter, 1 = expecting a value*/

wantout = false;
while (curr_char != "}")
{
    
    /*Skip whitespace*/
    str_pos = json_skip_whitespace(str,str_pos);
    if (str_pos = -1)
    {
        with (obj) instance_destroy();
        return -1;
    }
    
    curr_char = string_char_at(str,str_pos);
    /*Begin Parsing*/
    switch (expect)
    {
        case 0: /*Expecting a parameter*/
            switch (json_char_type(curr_char))
            {
                case 1:
                    param = json_parse_number(str,str_pos,'new_pos');
                    str_pos = variable_local_get('new_pos');
                    if (str_pos = -1)
                    {
                        with (obj) instance_destroy();
                        return -1;
                    }
                    break;
                case 2:
                    param = json_parse_string(str,str_pos,'new_pos');
                    str_pos = variable_local_get('new_pos');
                    if (str_pos = -1)
                    {
                        with (obj) instance_destroy();
                        return -1;
                    }
                    break;
                default:
                    json_debug("Unknown parameter type at position "+string(str_pos));
                    with (obj) instance_destroy();
                    return -1;
                    break;
            }
            /*Skip over the colon*/
            str_pos = json_skip_delimiter(str,str_pos);
            if (str_pos = -1)
            {
                with (obj) instance_destroy();
                return -1;
            }
            expect = 1; /*Expect a value on next iteration*/
            break;
            /*Expect parameter end*/

        case 1: /*Expecting a value*/
            switch (json_char_type(curr_char))
            {
                
                case 1:
                    value = json_parse_number(str,str_pos,'new_pos');
                    str_pos = variable_local_get('new_pos');
                    if (str_pos = -1)
                    {
                        with (obj) instance_destroy();
                        return -1;
                    }
                    value = json_new_object("val",value);
                    break;
                case 2:
                    value = json_parse_string(str,str_pos,'new_pos');
                    str_pos = variable_local_get('new_pos');
                    if (str_pos = -1)
                    {
                        with (obj) instance_destroy();
                        return -1;
                    }
                    value = json_new_object("val",value);
                    break;
                case 3:
                    value = json_parse_object(str,str_pos,'new_pos');
                    str_pos = variable_local_get('new_pos');
                    if (str_pos = -1)
                    {
                        with (obj) instance_destroy();
                        return -1;
                    }
                    break;
                case 4:
                    value = json_parse_array(str,str_pos,'new_pos');
                    str_pos = variable_local_get('new_pos');
                    if (str_pos = -1)
                    {
                        with (obj) instance_destroy();
                        return -1;
                    }
                    break;
                default:
                    with (obj) instance_destroy();
                    return -1;
                    break;
            } /*Expect value end*/
            
            /*Skip over the comma*/
            temp = json_skip_comma(str,str_pos);
            if (temp = -1) 
            /*If it returns an error, it might because we're finished parsing*/
            {
                str_pos = json_skip_whitespace(str,str_pos);
                if (str_pos = -1)
                /*Unexpected end of string*/
                {
                    with (obj) instance_destroy();
                    return -1;
                }
                if (string_char_at(str,str_pos) != "}") 
                /*It's an unexpected character*/
                {
                    json_debug("Unexpected character '"+string_char_at(str,str_pos)+"' at "+string(str_pos));
                    with (obj) instance_destroy();
                    return -1;
                }
                wantout = true; 
                /*We're done parsing at this point so we should break
                out of the loop*/
            }else{
                str_pos = temp;
            }
            
            /*There is more to parse*/
            expect = 0;/*Expect a parameter on the next iteration*/
            json_add_value(obj,"object",value,param);
            break;
        default:
            /*Didn't know what to expect*/
            json_debug("Expect value retarded");
            with (obj) instance_destroy();
            return -1;
            break;
    }
    
    if (wantout)
    {
        break;
    }
}

variable_local_set(strpos_ptr, str_pos+1);
return obj;

#define json_parse_array
/*
Copyright (c) 2010, Daniel Tang
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
Returns: -1 on failure, array pointer on success

Stack return:
    [0] New string position push'd first
*/
var str, strpos_ptr, str_pos, level, obj, curr_char, temp;
str = argument0;
str_pos = real(argument1);
strpos_ptr = string(argument2);

str_pos = json_skip_whitespace(str,str_pos);
if (str_pos = -1)
{
    return -1;
}


if (json_char_type(string_char_at(str,str_pos)) != 4)
{
    return -1;
}
str_pos+=1;


obj = json_new_object("arr"); /*Create an object at current level*/
curr_char = "";

while (curr_char != "]")
{
    str_pos = json_skip_whitespace(str,str_pos);
    if (str_pos = -1)
    {
        with (obj) instance_destroy();
        return -1;
    }
    curr_char = string_char_at(str,str_pos);

    switch (json_char_type(curr_char))
    {
        case 1:
                value = json_parse_number(str,str_pos,'new_pos');
                str_pos = variable_local_get('new_pos');
                if (str_pos = -1)
                {
                    with (obj) instance_destroy();
                    return -1;
                }
                value = json_new_object("val",value);
                break;
        case 2:
                value = json_parse_string(str,str_pos,'new_pos');
                str_pos = variable_local_get('new_pos');
                if (str_pos = -1)
                {
                    with (obj) instance_destroy();
                    return -1;
                }
                value = json_new_object("val",value);
                break;
        case 3:
                value = json_parse_object(str,str_pos,'new_pos');
                str_pos = variable_local_get('new_pos');
                if (value = -1)
                {
                    with (obj) instance_destroy();
                    return -1;
                }
                break;
        case 4:
                value = json_parse_array(str,str_pos,'new_pos');
                str_pos = variable_local_get('new_pos');
                if (value = -1)
                {
                    with (obj) instance_destroy();
                    return -1;
                }
                break;
        default:
                with (obj) instance_destroy();
                return -1;
                break;
    }
    
    json_add_value(obj,"object",value);
    temp = json_skip_comma(str,str_pos);
    /*If skipping the comma failed, we might have reached the end of the array*/
    if (temp = -1)
    {
        json_skip_whitespace(str,str_pos);
        if (str_pos = -1)
        /*Unexpected end of string*/
        {
            with (obj) instance_destroy();
            return -1;
        }
        if (string_char_at(str,str_pos) != "]")
        {
            json_debug("Unexpected character '"+string_char_at(str,str_pos)+"' at "+string(str_pos));
            with (obj) instance_destroy();
            return -1;
        }
        break; /*We're done processing the array. Break out of loop*/
    }else{
        str_pos = temp;
    }

}

variable_local_set(strpos_ptr,str_pos+1); /*Skip over ]*/
return obj;
    


#define json_parse
/*
Copyright (c) 2010, Daniel Tang
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

var obj, str, str_pos;
str = argument0;
str_pos = json_skip_whitespace(str,1);

if (str_pos = -1)
{
    return -1;
}

switch (json_char_type(str))
{
    case 3:
        obj = json_parse_object(str,str_pos,'new_pos');
        break;
    case 4:
        obj = json_parse_array(str,str_pos,'new_pos');
        break;
    default:
        return -1;
}

return obj;

#define json_new_object
/*
Copyright (c) 2010, Daniel Tang
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*Returns -1 on failure*/
json_init();

var obj,type,value;
type = argument0;
value = argument1; /*Only used for nodevalue*/

obj = instance_create(-1,-1,global.___json);

switch (type)
{
    case "obj":
        obj.type = "obj";
        obj.keyvalue = ds_map_create();
        break;
    case "arr":
        obj.type = "arr";
        obj.keyvalue = ds_list_create();
        break;
    case "val":
        obj.type = "val";
        obj.keyvalue = value;
        break;
    default:
        json_debug("Trying to assign invalid type '"+type+"' to object");
        with (obj) instance_destroy();
        return -1;
}

return obj;

#define json_add_value
/*
Copyright (c) 2010, Daniel Tang
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/*
Add value to a JSON object

Args:
    0 = object
    1 = type ("object" if you're assigning an object. empty string if assigning a literal value)
    2 = value
    3 = key
    
Returns nothing;
*/

var obj, type, value, key;
obj = real(argument0);
type = argument1;
value = argument2; /*Unless assigning to a nodevalue, this MUST be a object pointer*/
key = argument3; /*Only used for objects*/

if (!instance_exists(obj) || string(obj.type) = '')
{
    json_debug("Invalid or non-existent object passed to json_add_value. Returning -1");
    return -1;
}

if (type = "object")
{
    switch (obj.type)
    {
        case "obj":
            ds_map_add(obj.keyvalue,key,real(value));
            break;
        case "arr":
            ds_list_add(obj.keyvalue,real(value));
            break;
        case "val":
            obj.keyvalue = value;
            break;
        default:
            json_debug("Invalid or non-existent object passed to json_add_value. Returning -1");
            return -1;
    }
    return 0;
}else{
    return json_add_value(obj, "object", json_new_object("val",value), key );
}

#define json_dump
/*
Copyright (c) 2010, Daniel Tang
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/*
Dump out the object.

Args:
    0 = Object;
    1 = (reserved) always an empty string;

Returns string that can be drawn to screen
*/

var obj, str, i, addspace, curr_key;

obj = argument0;
addspace = string(argument1);

if (addspace = "0")
{
    addspace = "";
}

//str=addspace+"Dumping object \#"+string(obj)+"#";
str="";

switch (obj.type)
{
    case "obj":
        curr_key = json_first_key(obj);
        for (i=0; i< json_obj_size(obj);i+=1)
        {
            str+=addspace+"Key:       "+string_replace(json_escape(string(curr_key)),"#","\#")+"#";
            str+=addspace+"Value:     \#"+string_replace(json_escape(string(json_surf(obj,curr_key))),"#","\#")+"#";
            str+=json_dump(json_surf(obj,curr_key),addspace+"  ");
            curr_key = json_next_key(obj,curr_key);
        }
        break;
    case "arr":
        for (i=0; i< json_obj_size(obj);i+=1)
        {
            str+=addspace+"Key:       "+string(i)+"#";
            str+=addspace+"Value:     \#"+string_replace(json_escape(string(json_surf(obj,i))),"#","\#")+"#";
            str+=json_dump(json_surf(obj,i,true),addspace+"  ");
        }
        break;
    case "val":
        str+=addspace+'Value:     '+string_replace(json_escape(string(json_value(obj))),"#","\#")+"#";
        break;
}
//str+=orig_addspace+"Dump End \#"+string(obj)+"#";
return str;

#define json_first_key
/*
Get the first key in a JSON object
*/

var obj;

obj = argument0;

if (obj.type != "obj")   return -1;

return ds_map_find_first(obj.keyvalue);

#define json_next_key
/*
Get the next key in the JSON object
*/

var obj, curr_key;

obj = argument0;
curr_key = argument1;

if (obj.type != "obj")   return -1;

return ds_map_find_next(obj.keyvalue, curr_key);

#define json_obj_size
/*
Returns the size of the object
*/

var obj;
obj = argument0;

switch (obj.type)
{
    case "obj":
        return ds_map_size(obj.keyvalue);
    case "arr":
        return ds_list_size(obj.keyvalue);
    case "val":
        return 1;
    default:
        return -1;
}

